package com.alstudio.utils.android.media.audio.recorder;

import java.io.File;

import android.content.Context;
import android.media.MediaRecorder;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OnInfoListener;
import android.os.Handler;
import android.text.TextUtils;

import com.alstudio.utils.android.ALFileManager;
import com.alstudio.utils.android.ALSdcardManager;

/**
 * ALAudioRecorder实现从MIC录音的功能。输出的音频格式为AMR，编码格式为AMR_NB。
 * 通过构造方法可设定录音文件保存路径、录音文件名、最大录音时间。如果未设定录音文件名，将构造一个缺省的文件名。命名规则为:"audio_ + 当前时间  + .amr "
 * 通过回调方法可得到录音开始、录音通知、录音失败、录音时长变化、录音振幅大小变化等信息。通过相关方法可得到录音文件的存储路径。
 * 	
 * 请注意，调用start方法开始录音时，将会删除已录制的录音文件(如果有的话)！
 * 
 * @author Alonso Lee
 * 
 */
public class ALAudioRecorder implements ALAudioRecorderEngine {

	// 刷新录音时长间隔时间
	private final int UPDATE_AUDIO_TIME_PERIOD = 1000;
	// 刷新音频振幅间隔时间
	private final int UPDATE_AUDIO_AMPLITUDE_PERIOD = 250;

	// 缺省无效的最大录音时长
	private final int INVALID_MAX_DURATION = -1;
	// 缺省录音时长为1秒
	private final int BEGIN_SECOND = 1;

	// 音频文件保存路径
	private String mPath;
	// 音频文件名
	private String mName;

	private Context mContext;
	// 最大录音时长
	private int mMaxDuration = INVALID_MAX_DURATION;
	// 当前录音时长
	private int currentRecordTime;

	private ALAudioRecordCallback mCallback;
	// 录音设备对象
	private MediaRecorder mRecorder;
	// 录音文件保存目录
	private String outputPath;
	// 正在录音标志位
	private boolean isRecording;
	// 用于刷新录音时间和振幅强度
	private Handler mHandler;

	// 缺省音频文件名前缀
	private final String OUTPUT_FILE_NAME_PREFIX = "audio_";
	// 缺省音频文件名后缀
	private final String OUTPUT_FILE_NAME_SUFFIX = ".amr";

	/**
	 * 定时1秒回调更新录音时间
	 */
	private Runnable mUpdateTimeTask = new Runnable() {
		public void run() {

			if (mCallback != null) {
				if (mRecorder != null) {
					mCallback.onAudioProgressChanged(currentRecordTime);
				}
			}
			currentRecordTime++;
			mHandler.postDelayed(this, UPDATE_AUDIO_TIME_PERIOD);
		}
	};

	/**
	 * 定时250毫秒刷新振幅强度
	 */
	private Runnable mUpdateAmpTask = new Runnable() {

		@Override
		public void run() {
			// TODO Auto-generated method stub
			if (mCallback != null) {
				if (mRecorder != null) {
					mCallback.onAudioAmplitudeChanged(mRecorder
							.getMaxAmplitude());
				}
			}
			mHandler.postDelayed(this, UPDATE_AUDIO_AMPLITUDE_PERIOD);
		}
	};

	/**
	 * 构造函数，构建录音对象
	 * 
	 * @param context
	 *            上下文
	 * @param path
	 *            音频文件保存路径
	 * @param fileName
	 *            音频文件名
	 * @param callback
	 *            回调方法
	 * @author Alonso Lee
	 */
	public ALAudioRecorder(Context context, String path, String fileName,
			ALAudioRecordCallback callback) {
		// TODO Auto-generated constructor stub

		mPath = path;
		mName = fileName;
		mContext = context;
		mHandler = new Handler();
		mCallback = callback;
	}

	/**
	 * 构造函数，构建录音对象
	 * 
	 * @param context
	 *            上下文
	 * @param path
	 *            音频文件保存路径
	 * @param fileName
	 *            音频文件名
	 * @param maxDuration
	 *            最大录音时长
	 * @param callback
	 *            回调方法
	 * @author Alonso Lee
	 * 
	 */
	public ALAudioRecorder(Context context, String path, String fileName,
			int maxDuration, ALAudioRecordCallback callback) {
		// TODO Auto-generated constructor stub

		mPath = path;
		mName = fileName;
		mContext = context;
		mHandler = new Handler();
		mCallback = callback;
		mMaxDuration = maxDuration;
	}

	/**
	 * 初始化录音环境
	 * 
	 * @author Alonso Lee
	 */
	private void build() {

		if (ALSdcardManager.isExtStorageAvailable()) {
			// 无SD卡
			if (mCallback != null) {
				mCallback
						.onAudioError(ALAudioRecordErroCode.AL_AUDIO_RECORD_TYPE_ERROR_NO_EXTENAL_STORAGE);
			}
			return;
		}

		mRecorder = new MediaRecorder();
		// 设定音源
		mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
		// 设置输出格式
		mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
		// 设置语音编码
		mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

		// 设定最大录音时长
		if (mMaxDuration != INVALID_MAX_DURATION) {
			mRecorder.setMaxDuration(mMaxDuration);
		}

		// 检测录音文件保存路径是否有效
		File dir = new File(mPath);

		if (!ALFileManager.isFileExsit(mPath)) {
			dir.mkdirs();
		}

		// 检测录音文件名是否有效
		if (TextUtils.isEmpty(mName)) {
			// 没有指定文件名，使用缺省的文件名，命名规则:"audio_ + 当前时间  + .amr "
			mName = OUTPUT_FILE_NAME_PREFIX + System.currentTimeMillis()
					+ OUTPUT_FILE_NAME_SUFFIX;
		}

		File outputFile = new File(dir, mName);

		// 删除已有的文件
		outputFile.deleteOnExit();

		// 设置录音文件路径
		outputPath = outputFile.getAbsolutePath();

		mRecorder.setOutputFile(outputFile.getAbsolutePath());

		mRecorder.setOnErrorListener(new OnErrorListener() {

			@Override
			public void onError(MediaRecorder mr, int what, int extra) {
				// TODO Auto-generated method stub
				// 录音出现错误，释放录音资源
				doStop();
				// 回调通知录音错误
				if (mCallback != null) {
					mCallback
							.onAudioError(ALAudioRecordErroCode.AL_AUDIO_RECORD_TYPE_ERROR_UNKONW);
				}
			}
		});

		mRecorder.setOnInfoListener(new OnInfoListener() {

			@Override
			public void onInfo(MediaRecorder mr, int what, int extra) {
				// TODO Auto-generated method stub
				// 录音完成，释放录音资源
				doStop();

				switch (what) {
				case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
					// 回调通知录音时长已满
					if (mCallback != null) {
						mCallback.onAudioMaxDurationReached();
					}
					break;
				}
			}
		});

		try {
			// 初始化录音对象
			mRecorder.prepare();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			mRecorder.release();
			mRecorder = null;
			// 回调通知初始化失败
			if (mCallback != null) {
				mCallback
						.onAudioError(ALAudioRecordErroCode.AL_AUDIO_RECORD_TYPE_ERROR_INIT_RECORDER_FAIL);
			}
		}

	}

	@Override
	public void start() {
		// TODO Auto-generated method stub
		// 开始录音
		doStart();
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub
		// 停止录音
		doStop();
	}

	@Override
	public String getFilePath() {
		// TODO Auto-generated method stub
		return outputPath;
	}

	@Override
	public boolean isRecording() {
		// TODO Auto-generated method stub
		return isRecording;
	}

	@Override
	public void setMaxDuration(int max_duration_ms) {
		// TODO Auto-generated method stub
		mMaxDuration = max_duration_ms;
	}

	@Override
	public int getMaxDuration() {
		// TODO Auto-generated method stub
		return mMaxDuration;
	}

	/**
	 * 执行开始录音
	 * 
	 * @author Alonso Lee
	 */
	private void doStart() {

		// 正在录音，直接返回
		if (isRecording)
			return;

		if (mRecorder == null) {
			// 初始化录音对象
			build();
		}

		// 开始录音
		isRecording = true;
		mRecorder.start();
		mHandler.removeCallbacks(mUpdateTimeTask);
		mHandler.removeCallbacks(mUpdateAmpTask);
		currentRecordTime = BEGIN_SECOND;
		mHandler.postDelayed(mUpdateTimeTask, UPDATE_AUDIO_TIME_PERIOD);
		mHandler.postDelayed(mUpdateAmpTask, UPDATE_AUDIO_AMPLITUDE_PERIOD);

	}

	/**
	 * 执行停止录音
	 * 
	 * @author Alonso Lee
	 * 
	 */
	private void doStop() {

		isRecording = false;

		//释放录音对象
		if (mRecorder != null) {
			mRecorder.stop();
			mRecorder.release();
			mRecorder = null;
		}
		
		//移除时间与振幅刷新回调
		mHandler.removeCallbacks(mUpdateTimeTask);
		mHandler.removeCallbacks(mUpdateAmpTask);

		//回调通知录音停止
		if (mCallback != null) {
			mCallback.onStop();
		}
	}

	public enum ALAudioRecordErroCode {

		// 初始化录音器失败
		AL_AUDIO_RECORD_TYPE_ERROR_INIT_RECORDER_FAIL,
		// 未挂载外部存储器
		AL_AUDIO_RECORD_TYPE_ERROR_NO_EXTENAL_STORAGE,
		// 未知错误
		AL_AUDIO_RECORD_TYPE_ERROR_UNKONW,

	}

}
